
local lcf = ffi.C

local o = {}

redis = o


local sf = string.format
local str_constructor = ffi.string
local str_match = string.match
local m_copy = ffi.copy


local function parse_reply(c_reply)
	if 1==c_reply.type or 5==c_reply.type then
		return str_constructor(c_reply.str,c_reply.len)
	elseif 3==c_reply.type then
		return tonumber(c_reply.integer)
	elseif 2==c_reply.type then
		local t = {}
		-- 目前仅实现一层，应该也够了
		for i=0,tonumber(c_reply.elements)-1 do
			local aa = parse_reply(c_reply.element[i])
			if 'table'==type(aa) then
				for j=0,#aa do
					table.insert(t,aa[j])
				end
			else
				table.insert(t,aa)
			end
		end
		return t
	elseif 4==c_reply.type then
		return nil
	elseif 6==c_reply.type then
		print(str_constructor(c_reply.str,c_reply.len))
		return nil
	end
end


o.well_buf = ffi.new('char[?]', 1024*16)	-- a 16K buffer to format commands. need bigger? modify here


-- 更适合lua的版本。相比原先的更加优化和安全
function o.command_and_wait(redis_index, cmd, ...)
--[[
*<number of arguments> CR LF
$<number of bytes of argument 1> CR LF
<argument data> CR LF
...
$<number of bytes of argument N> CR LF
<argument data> CR LF
例子如下：
*3
$3
SET
$5
mykey
$7
myvalue
--]]
	if nil==cmd or redis_index<0 then
		return nil
	end

	local pa = { ... }

	local crlf = '\r\n'
	local well = o.well_buf
	local well_offset = 0

	local m_cat = function(lua_str)
		lua_str = tostring(lua_str)
		m_copy(well+well_offset, lua_str)
		well_offset = well_offset + #lua_str
	end

	m_cat('*')
	m_cat(#pa+1)
	m_cat(crlf)
	m_cat('$')
	m_cat(#cmd)
	m_cat(crlf)
	m_cat(cmd)
	m_cat(crlf)

	for i=1,#pa do
		local ele = tostring(pa[i])
		m_cat('$')
		m_cat(#ele)
		m_cat(crlf)
		m_cat(ele)
		m_cat(crlf)
	end

	local reply = gx_uni_call(3, redis_index, str_constructor(well, well_offset))
	
	if nil==reply then
		return nil
	end
	
	reply = ffi.cast('redisReply*',reply)
	local ret = parse_reply(reply)
	if 'table'~=type(ret) then
		return ret
	else
		return unpack(ret)
	end
end

function o.old_command_and_wait(redis_index,formatt,...)
	-- 做参数类型安全检查
	--[[
	local dd = 8
	local ee = ffi.cast('size_t',dd)
	print(type(ee))										cdata
	print(ffi.istype('size_t',ee),'size_t')	true
	print(ffi.istype('int',ee),'int')				false
	print(ffi.istype('uint32_t',ee),'uint32_t')	true
	--]]
	local pa = { ... }
	local offset = 0

	local first = str_match(formatt, '(%S+)')

	local cmd = sf('*%d\r\n', #pa+1)
	cmd = cmd..sf('$%d\r\n%s\r\n', #first,first)

	for cc in string.gmatch(formatt,'%%(%a)') do
		if 'd'==string.lower(cc) then
			offset = offset+1
			if not ('number'==type(pa[offset]) or ffi.istype('size_t',pa[offset]) or ffi.istype('int',pa[offset])) then
				error('format check failed, need number')
			end

			local t = tostring(pa[offset])
			cmd = cmd..sf('$%d\r\n%s\r\n', #t,t)

			
		elseif 's'==string.lower(cc) then
			offset = offset+1
			if not ('string'==type(pa[offset])) then
				error('format check failed, need string')
			end

			local t = tostring(pa[offset])
			cmd = cmd..sf('$%d\r\n%s\r\n', #t,t)

			
		elseif 'b'==string.lower(cc) then
			offset = offset+2
			local c = pa[offset-1]
			local lenth = pa[offset]
			if not ('string'==type(c) or ffi.istype('char*',c)) then
				error('format check failed, need string and size_t')
			end
			if not ('number'==type(lenth) or ffi.istype('size_t',lenth) or ffi.istype('int',lenth)) then
				error('format check failed, need string and size_t')
			end

			local ss = str_constructor(c, lenth)
			cmd = cmd..sf('$%d\r\n%s\r\n', #ss,ss)
		else
			error('unsupported format '..cc)
		end
	end
	
	local reply = gx_uni_call(3, redis_index, cmd)
	
	if nil==reply then
		return nil
	end
	
	reply = ffi.cast('redisReply*',reply)
	local ret = parse_reply(reply)
	if 'table'~=type(ret) then
		return ret
	else
		return unpack(ret)
	end
	
end


function o.get(redis_index,key)
	key = tostring(key)
	return o.command_and_wait(redis_index,'GET',key)
end

function o.set(redis_index,key,value)
	key = tostring(key)
	value = tostring(value)
	return o.command_and_wait(redis_index,'SET',key,value)
end

